/*
 * @Author: Wangtao
 * @Date: 2022-07-26 14:26:31
 * @LastEditors: Wangtao
 * @LastEditTime: 2022-08-23 17:23:46
 */
import { Directive, watchPostEffect, h, render, Fragment, shallowReactive, normalizeStyle } from 'vue'
import { itemPreCls, itemRefPrecls, Props } from './interface'
import resizeobserver from './resizeobserver'
import { getLoading, getClass, patchClass, allClass } from './utils'
import { stringifyStyle } from '@vue/shared'

const setup = (el: any, binding: any, vnode: any) => {
  const props: any = shallowReactive({
    el,
    binding,
    fragment: el.appendChild(document.createElement('div')),
    targets: shallowReactive([]),
    vnode,
  })

  el = null
  binding = null
  vnode = null

  const unwatch1 = watchPostEffect(() => {
    const { el, binding, vnode } = props
    if (getLoading(binding)) {
      patchClass(el, getClass(binding), allClass(binding))
      el.style.cssText = stringifyStyle(normalizeStyle([el.style.cssText, binding.value.loadingStyle]))
    } else {
      el.classList.remove(...allClass(binding).split(' '))
      el.style.cssText = stringifyStyle(vnode.props?.style)
    }
  })

  const unwatch2 = watchPostEffect(() => {
    const { binding, fragment } = props
    const targets: any = props.targets, loading = getLoading(binding)
    // loading 状态才使用 resizeobserver
    if (loading) resizeobserver.observer(props.el)
    else resizeobserver.unobserve(props.el)

    if (loading) {
      targets.forEach((e: any) => e.el.classList.add(itemRefPrecls))
    } else {
      targets.forEach((e: any) => e.el.classList.remove(itemRefPrecls))
    }

    // 渲染 skeleton-item
    if (loading) {
      const items = targets.map((e: any) => h('div', {
        class: [props.binding.value.class, e.binding.value?.class, itemPreCls],
        style: [props.binding.value.style, e.binding.value?.style, e.state.style]
      }))
      render(h(Fragment, items), fragment)
    } else {
      render(null, fragment)
    }
  })

  props.unwatch = () => {
    unwatch1()
    unwatch2()
  }

  return props
}


export const skeletonList: Props[] = []

const Skeleton: Directive = {
  created (el, binding, vnode) {
    skeletonList.push(setup(el, binding, vnode))
  },
  beforeMount (el, binding, vnode) {
    const props: any = skeletonList.find(e => e.el === el)
    props.binding = binding
    props.vnode = vnode
  },
  beforeUpdate (el, binding, vnode) {
    const props: any = skeletonList.find(e => e.el === el)
    props.vnode = vnode
    props.binding = binding
  },
  beforeUnmount (el) {
    const i = skeletonList.findIndex(e => e.el === el)
    if (i < 0) return
    const props: any = skeletonList.splice(i, 1)[0]
    props.unwatch()
    props.fragment.parentNode.removeChild(props.fragment)
    resizeobserver.dispose(el)
  }
}

export default Skeleton